﻿#region using...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using NsMenu = Infusion.Silverlight.Controls.Menu;
#endregion

namespace Infusion.Silverlight.Controls.Ribbon
{
    #region Delegates...
    public delegate void ButtonClickEventHandler(object sender, MouseButtonEventArgs e);
    #endregion

    #region : ButtonFormat :
    public enum ButtonFormat
    {
        Large,
        SmallWithText,
        Small
    }
    #endregion

    #region : ButtonMenuStyle :
    public enum ButtonMenuStyle
    {
        None,
        Simple,
        Split
    }
    #endregion

    #region : RibbonButton :
    #region Template Parts...
    [TemplatePart(Name = RibbonButton.RibbonButtonLayoutRoot, Type = typeof(Canvas))]
    [TemplatePart(Name = RibbonButton.CanvasMenuSplit, Type = typeof(Canvas))]
    [TemplatePart(Name = RibbonButton.CanvasMenuSplitClickSurface, Type = typeof(Canvas))]
    [TemplatePart(Name = RibbonButton.CanvasClickSurface, Type = typeof(Canvas))]
    [TemplatePart(Name = RibbonButton.RectOuterBorder, Type = typeof(Rectangle))]
    [TemplatePart(Name = RibbonButton.RectInnerBorder, Type = typeof(Rectangle))]
    [TemplatePart(Name = RibbonButton.PathOuterBorder, Type = typeof(Path))]
    [TemplatePart(Name = RibbonButton.PathInnerBorder, Type = typeof(Path))]
    [TemplatePart(Name = RibbonButton.GridLargeButtonLayout, Type = typeof(Grid))]
    [TemplatePart(Name = RibbonButton.GridSmallButtonLayout, Type = typeof(Grid))]
    [TemplatePart(Name = RibbonButton.ImgLargeIcon, Type = typeof(Image))]
    [TemplatePart(Name = RibbonButton.ImgSmallIcon, Type = typeof(Image))]
    [TemplatePart(Name = RibbonButton.TxtFirstLine, Type = typeof(TextBlock))]
    [TemplatePart(Name = RibbonButton.TxtSecondLine, Type = typeof(TextBlock))]
    [TemplatePart(Name = RibbonButton.TxtText, Type = typeof(TextBlock))]
    [TemplatePart(Name = RibbonButton.StackSecondLineAndArrow, Type = typeof(StackPanel))]
    [TemplatePart(Name = RibbonButton.RibbonButtonLayoutRootClip, Type = typeof(RectangleGeometry))]
    [TemplatePart(Name = RibbonButton.PathMenuSplitClipFigure, Type = typeof(PathFigure))]
    #endregion
    public class RibbonButton : RibbonTabGroupItem
    {
        #region Events...
        public event ButtonClickEventHandler ButtonClick;
        #endregion

        #region TemplatePart Names...
        public const string RibbonButtonLayoutRoot = "RibbonButtonLayoutRoot";
        public const string CanvasMenuSplit = "CanvasMenuSplit";
        public const string CanvasMenuSplitClickSurface = "CanvasMenuSplitClickSurface";
        public const string CanvasClickSurface = "CanvasClickSurface";
        public const string RectOuterBorder = "RectOuterBorder";
        public const string RectInnerBorder = "RectInnerBorder";
        public const string PathOuterBorder = "PathOuterBorder";
        public const string PathInnerBorder = "PathInnerBorder";
        public const string GridLargeButtonLayout = "GridLargeButtonLayout";
        public const string GridSmallButtonLayout = "GridSmallButtonLayout";
        public const string ImgLargeIcon = "ImgLargeIcon";
        public const string ImgSmallIcon = "ImgSmallIcon";
        public const string ImgMenuDropDownArrowLarge = "ImgMenuDropDownArrowLarge";
        public const string ImgMenuDropDownArrowSmall = "ImgMenuDropDownArrowSmall";
        public const string TxtFirstLine = "TxtFirstLine";
        public const string TxtSecondLine = "TxtSecondLine";
        public const string TxtText = "TxtText";
        public const string StackSecondLineAndArrow = "StackSecondLineAndArrow";
        public const string RibbonButtonLayoutRootClip = "RibbonButtonLayoutRootClip";
        public const string PathMenuSplitClipFigure = "PathMenuSplitClipFigure";
        public const string PathMenuSplitOuterBorderFigure = "PathMenuSplitOuterBorderFigure";
        public const string PathMenuSplitInnerBorderFigure = "PathMenuSplitInnerBorderFigure";
        #endregion

        #region TemplatePart Variables...
        private Canvas ribbonButtonLayoutRoot;
        private Canvas canvasMenuSplit;
        private Canvas canvasMenuSplitClickSurface;
        private Canvas canvasClickSurface;
        private Rectangle rectOuterBorder;
        private Rectangle rectInnerBorder;
        private Path pathOuterBorder;
        private Path pathInnerBorder;
        private Grid gridLargeButtonLayout;
        private Grid gridSmallButtonLayout;
        private Image imgLargeIcon;
        private Image imgSmallIcon;
        private Image imgMenuDropDownArrowLarge;
        private Image imgMenuDropDownArrowSmall;
        private TextBlock txtFirstLine;
        private TextBlock txtSecondLine;
        private TextBlock txtText;
        private StackPanel stackSecondLineAndArrow;
        private RectangleGeometry ribbonButtonLayoutRootClip;
        private PathFigure pathMenuSplitClipFigure;
        private PathFigure pathMenuSplitOuterBorderFigure;
        private PathFigure pathMenuSplitInnerBorderFigure;
        #endregion

        #region Class Variables...
        private Ribbon parentRibbon = null;
        private bool templateApplied = false;
        private bool dirtyTextLayout = false;
        private bool mouseOver = false;
        private string menuID = string.Empty;
        private DispatcherTimer toolTipTimer;
        private Point toolTipLocation;
        private Point menuLocation;
        private NsMenu.Menu menuObject = null;
        private bool toolTipUseAltDescription = false;
        #endregion

        #region Constants...
        private double LargeButtonHeight = 65;
        private double SmallButtonHeight = 22;
        #endregion

        #region Dependency Properties...

        #region TextProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.Text dependency property.
        /// </summary>
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(RibbonButton),
                new PropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
        #endregion

        #region LargeImageSourceProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.LargeImageSource dependency property.
        /// </summary>
        public static readonly DependencyProperty LargeImageSourceProperty = DependencyProperty.Register(
            "LargeImageSource",
            typeof(string),
            typeof(RibbonButton),
            new PropertyMetadata(new PropertyChangedCallback(LargeImageSourcePropertyChanged)));
        #endregion

        #region SmallImageSourceProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.SmallImageSource dependency property.
        /// </summary>
        public static readonly DependencyProperty SmallImageSourceProperty = DependencyProperty.Register(
            "SmallImageSource",
            typeof(string),
            typeof(RibbonButton),
            new PropertyMetadata(new PropertyChangedCallback(SmallImageSourcePropertyChanged)));
        #endregion

        #region SmallImageWidthProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.SmallImageWidth dependency property.
        /// </summary>
        public static readonly DependencyProperty SmallImageWidthProperty = DependencyProperty.Register(
            "SmallImageWidth",
            typeof(double),
            typeof(RibbonButton),
            null);
        #endregion

        #region MenuIDProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.MenuID dependency property.
        /// </summary>
        public static readonly DependencyProperty MenuIDProperty = DependencyProperty.Register(
                "MenuID",
                typeof(string),
                typeof(RibbonButton),
                new PropertyMetadata(new PropertyChangedCallback(MenuIDPropertyChanged)));
        #endregion

        #region FormatProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.Format dependency property.
        /// </summary>
        public static readonly DependencyProperty FormatProperty = DependencyProperty.Register(
                "Format",
                typeof(ButtonFormat),
                typeof(RibbonButton),
                new PropertyMetadata(new PropertyChangedCallback(FormatPropertyChanged)));
        #endregion

        #region ToolTipIDProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.RibbonButton.ToolTipID dependency property.
        /// </summary>
        public static readonly DependencyProperty ToolTipIDProperty = DependencyProperty.Register(
                "ToolTipID",
                typeof(string),
                typeof(RibbonButton),
                null);
        #endregion

        #region CommandProperty
        /// <summary>
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
                "Command",
                typeof(ICommand),
                typeof(RibbonButton),
                new PropertyMetadata(OnCommandChanged));
        #endregion

        #region CommandParameterProperty
        /// <summary>
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
                "CommandParameter",
                typeof(object),
                typeof(RibbonButton),
                null);
        #endregion

        #endregion

        #region Dependency Property Changed Handlers...
        #region TextPropertyChanged
        private static void TextPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
        }
        #endregion

        #region LargeImageSourcePropertyChanged
        private static void LargeImageSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
        }
        #endregion

        #region SmallImageSourcePropertyChanged
        private static void SmallImageSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
        }
        #endregion

        #region MenuIDPropertyChanged
        private static void MenuIDPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
        }
        #endregion

        #region FormatPropertyChanged
        private static void FormatPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
        }
        #endregion
        #endregion

        #region Constructor(s)...
        public RibbonButton()
            : base()
        {
            DefaultStyleKey = typeof(RibbonButton);

            canExecuteChangedHandler = Command_CanExecuteChanged;

            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(RibbonButton_IsEnabledChanged);

            this.IsEnabled = false;
            toolTipTimer = new DispatcherTimer();
            toolTipTimer.Interval = new TimeSpan(0, 0, 1);
            toolTipTimer.Tick += new EventHandler(toolTipTimer_Tick);
        }

        private void RibbonButton_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.Opacity = (this.IsEnabled) ? 1d : 0.5d;
        }

        #endregion

        #region Event Handlers...
        #region toolTipTimer_Tick
        private void toolTipTimer_Tick(object sender, EventArgs e)
        {
            toolTipTimer.Stop();

            if (!string.IsNullOrEmpty(ToolTipID) && ParentRibbon != null)
                parentRibbon.ShowToolTip(ToolTipID, toolTipLocation, toolTipUseAltDescription);
        }
        #endregion

        #region canvasClickSurface_MouseEnter
        private void canvasClickSurface_MouseEnter(object sender, MouseEventArgs e)
        {
            mouseOver = true;

            menuLocation = new Point(e.GetPosition(null).X - e.GetPosition(ribbonButtonLayoutRoot).X,
                e.GetPosition(null).Y - e.GetPosition(ribbonButtonLayoutRoot).Y + ActualHeight - 2);

            if (parentRibbon == null || !parentRibbon.HasMenuOpen)
            {
                toolTipTimer.Stop();

                if (parentRibbon != null)
                {
                    toolTipLocation = new Point(
                        e.GetPosition(null).X - e.GetPosition(ribbonButtonLayoutRoot).X,
                        parentRibbon.ActualHeight);
                }

                SetMouseOverVisualStyle(false);

                toolTipUseAltDescription = false;
                toolTipTimer.Start();
            }
        }
        #endregion

        #region canvasClickSurface_MouseLeave
        private void canvasClickSurface_MouseLeave(object sender, MouseEventArgs e)
        {
            mouseOver = false;

            if (parentRibbon == null || !parentRibbon.HasMenuOpen)
            {
                switch (Format)
                {
                    case ButtonFormat.Large:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);

                        canvasMenuSplit.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                        break;
                    case ButtonFormat.SmallWithText:
                    case ButtonFormat.Small:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);

                        canvasMenuSplit.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                        break;
                }

                toolTipTimer.Stop();

                if (parentRibbon != null)
                    parentRibbon.HideToolTip(ToolTipID);
            }
        }
        #endregion

        #region canvasClickSurface_MouseLeftButtonDown
        private void canvasClickSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            toolTipTimer.Stop();

            if (parentRibbon != null)
                parentRibbon.HideToolTip(ToolTipID);

            SetMouseOverVisualStyle(false);
            SetMouseDownVisualStyle(false);
        }
        #endregion

        #region canvasClickSurface_MouseLeftButtonUp
        private void canvasClickSurface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!string.IsNullOrEmpty(MenuID) && ButtonClick == null)
            {
                SetMouseOverVisualStyle(false);
                SetMouseDownVisualStyle(false);

                //if (parentRibbon != null)
                //{
                //  menuObject = parentRibbon.ShowMenu(MenuID, menuLocation, false);
                //  menuObject.Closing += new EventHandler(menuObject_Closing);
                //}
            }
            else
            {
                SetMouseOverVisualStyle(false);

                if (ButtonClick != null)
                    ButtonClick(this, e);
            }
            this.ExecuteCommand();
        }
        #endregion

        #region canvasMenuSplitClickSurface_MouseEnter
        private void canvasMenuSplitClickSurface_MouseEnter(object sender, MouseEventArgs e)
        {
            mouseOver = true;

            menuLocation = new Point(e.GetPosition(null).X - e.GetPosition(ribbonButtonLayoutRoot).X,
                e.GetPosition(null).Y - e.GetPosition(ribbonButtonLayoutRoot).Y + ActualHeight - 2);

            if (parentRibbon == null || !parentRibbon.HasMenuOpen)
            {
                toolTipTimer.Stop();

                if (parentRibbon != null)
                {
                    toolTipLocation = new Point(e.GetPosition(null).X - e.GetPosition(ribbonButtonLayoutRoot).X,
                        parentRibbon.ActualHeight);
                }

                SetMouseOverVisualStyle(true);

                toolTipUseAltDescription = true;
                toolTipTimer.Start();
            }
        }
        #endregion

        #region canvasMenuSplitClickSurface_MouseLeave
        private void canvasMenuSplitClickSurface_MouseLeave(object sender, MouseEventArgs e)
        {
            mouseOver = false;

            if (parentRibbon == null || !parentRibbon.HasMenuOpen)
            {
                ResetVisualStyle();

                toolTipTimer.Stop();

                if (parentRibbon != null)
                    parentRibbon.HideToolTip(ToolTipID);
            }
        }
        #endregion

        #region canvasMenuSplitClickSurface_MouseLeftButtonDown
        private void canvasMenuSplitClickSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (parentRibbon == null || !parentRibbon.HasMenuOpen)
            {
                toolTipTimer.Stop();
                if (parentRibbon != null)
                    parentRibbon.HideToolTip(ToolTipID);

                SetMouseDownVisualStyle(true);
            }
        }
        #endregion

        #region canvasMenuSplitClickSurface_MouseLeftButtonUp
        private void canvasMenuSplitClickSurface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (parentRibbon == null || !parentRibbon.HasMenuOpen)
            {
                if (!string.IsNullOrEmpty(MenuID))
                {
                    SetMouseOverVisualStyle(true);
                    SetMouseDownVisualStyle(true);

                    if (parentRibbon != null)
                    {
                        menuObject = parentRibbon.ShowMenu(MenuID, menuLocation, false);
                        menuObject.Closing += new EventHandler(menuObject_Closing);
                    }
                }
                else
                {
                    switch (Format)
                    {
                        case ButtonFormat.Large:
                            canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonMenuSplitHighlight);
                            pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonMenuSplitOuterBorder);
                            break;
                        case ButtonFormat.SmallWithText:
                        case ButtonFormat.Small:
                            canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonMenuSplitHighlight);
                            pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonMenuSplitOuterBorder);
                            break;
                    }
                }
            }
        }
        #endregion

        #region menuObject_Closing
        private void menuObject_Closing(object sender, EventArgs e)
        {
            if (!mouseOver)
                ResetVisualStyle();

            menuObject.Closing -= new EventHandler(menuObject_Closing);
            menuObject = null;
        }
        #endregion

        #region ribbonButtonLayoutRoot_LayoutUpdated
        private void ribbonButtonLayoutRoot_LayoutUpdated(object sender, EventArgs e)
        {
            if (dirtyTextLayout)
            {
                switch (Format)
                {
                    case ButtonFormat.Large:
                        {
                            double maxWidth = Math.Max(Math.Max(txtFirstLine.ActualWidth, txtSecondLine.ActualWidth + (string.IsNullOrEmpty(MenuID) ? 0d : 11d)), 37d) + 7d;

                            Width = maxWidth;
                            ribbonButtonLayoutRoot.Width = maxWidth;
                            ribbonButtonLayoutRootClip.Rect = new Rect(0, 0, maxWidth, LargeButtonHeight);

                            canvasClickSurface.Width = maxWidth;
                            canvasMenuSplit.Width = maxWidth;
                            canvasMenuSplit.SetValue(Canvas.LeftProperty, 0d);
                            canvasMenuSplitClickSurface.Width = maxWidth;
                            canvasMenuSplitClickSurface.SetValue(Canvas.LeftProperty, 0d);

                            gridLargeButtonLayout.ColumnDefinitions[0].Width = new GridLength(maxWidth);

                            pathMenuSplitClipFigure.Segments = GeneratePathSegments(true, pathMenuSplitClipFigure.StartPoint, canvasMenuSplitClickSurface.Width, canvasMenuSplitClickSurface.Height, 3d);
                            pathMenuSplitOuterBorderFigure.Segments = GeneratePathSegments(true, pathMenuSplitOuterBorderFigure.StartPoint, canvasMenuSplitClickSurface.Width, canvasMenuSplitClickSurface.Height, 3d);
                            pathMenuSplitInnerBorderFigure.Segments = GeneratePathSegments(true, pathMenuSplitInnerBorderFigure.StartPoint, canvasMenuSplitClickSurface.Width - 1d, canvasMenuSplitClickSurface.Height - 1d, 2.5d);

                            rectInnerBorder.Width = maxWidth - 1d;
                            rectOuterBorder.Width = maxWidth + 1d;

                            break;
                        }
                    case ButtonFormat.SmallWithText:
                    case ButtonFormat.Small:
                        {
                            double splitWidth = 9d;
                            double splitLeft = 3d;
                            double defaultWidth = 22d;

                            if (SmallImageWidth > 0d)
                                defaultWidth = SmallImageWidth;

                            double maxWidth = txtText.ActualWidth + (Format == ButtonFormat.Small ? 0d : 6d);
                            double width = maxWidth + defaultWidth + (ButtonMenuStyle == ButtonMenuStyle.None ? 0d : splitWidth);

                            Width = width;
                            ribbonButtonLayoutRoot.Width = width;
                            ribbonButtonLayoutRootClip.Rect = new Rect(0, 0, width, SmallButtonHeight);

                            canvasClickSurface.Width = width - (ButtonMenuStyle == ButtonMenuStyle.Split ? splitWidth + splitLeft : 0d);
                            canvasMenuSplit.Width = (ButtonMenuStyle != ButtonMenuStyle.Split ? 0d : splitWidth + splitLeft);
                            canvasMenuSplit.SetValue(Canvas.LeftProperty, maxWidth + defaultWidth - splitLeft);
                            canvasMenuSplitClickSurface.Width = (ButtonMenuStyle != ButtonMenuStyle.Split ? 0d : splitWidth + splitLeft);
                            canvasMenuSplitClickSurface.SetValue(Canvas.LeftProperty, maxWidth + defaultWidth - splitLeft);

                            gridSmallButtonLayout.ColumnDefinitions[0].Width = new GridLength(defaultWidth);
                            gridSmallButtonLayout.ColumnDefinitions[1].Width = new GridLength(maxWidth);

                            pathMenuSplitClipFigure.Segments = GeneratePathSegments(false, pathMenuSplitClipFigure.StartPoint, canvasMenuSplitClickSurface.Width, canvasMenuSplitClickSurface.Height, 3d);
                            pathMenuSplitOuterBorderFigure.Segments = GeneratePathSegments(false, pathMenuSplitOuterBorderFigure.StartPoint, canvasMenuSplitClickSurface.Width, canvasMenuSplitClickSurface.Height, 3d);
                            pathMenuSplitInnerBorderFigure.Segments = GeneratePathSegments(false, pathMenuSplitInnerBorderFigure.StartPoint, canvasMenuSplitClickSurface.Width - 1d, canvasMenuSplitClickSurface.Height - 1d, 2.5d);

                            if (ButtonMenuStyle != ButtonMenuStyle.None)
                            {
                                gridSmallButtonLayout.ColumnDefinitions[2].Width = new GridLength(splitWidth);
                            }
                            else
                            {
                                gridSmallButtonLayout.ColumnDefinitions[2].Width = new GridLength(0d);
                            }

                            rectInnerBorder.Width = width - 1;
                            rectOuterBorder.Width = width + 1;

                            break;
                        }
                }

                dirtyTextLayout = false;
            }
        }
        #endregion
        #endregion

        #region Methods...
        #region OnApplyTemplate
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (!templateApplied)
            {
                ribbonButtonLayoutRoot = (Canvas)GetTemplateChild(RibbonButtonLayoutRoot);
                canvasMenuSplit = (Canvas)GetTemplateChild(CanvasMenuSplit);
                canvasMenuSplitClickSurface = (Canvas)GetTemplateChild(CanvasMenuSplitClickSurface);
                canvasClickSurface = (Canvas)GetTemplateChild(CanvasClickSurface);
                rectOuterBorder = (Rectangle)GetTemplateChild(RectOuterBorder);
                rectInnerBorder = (Rectangle)GetTemplateChild(RectInnerBorder);
                pathOuterBorder = (Path)GetTemplateChild(PathOuterBorder);
                pathInnerBorder = (Path)GetTemplateChild(PathInnerBorder);
                gridLargeButtonLayout = (Grid)GetTemplateChild(GridLargeButtonLayout);
                gridSmallButtonLayout = (Grid)GetTemplateChild(GridSmallButtonLayout);
                imgLargeIcon = (Image)GetTemplateChild(ImgLargeIcon);
                imgSmallIcon = (Image)GetTemplateChild(ImgSmallIcon);
                imgMenuDropDownArrowLarge = (Image)GetTemplateChild(ImgMenuDropDownArrowLarge);
                imgMenuDropDownArrowSmall = (Image)GetTemplateChild(ImgMenuDropDownArrowSmall);
                txtFirstLine = (TextBlock)GetTemplateChild(TxtFirstLine);
                txtSecondLine = (TextBlock)GetTemplateChild(TxtSecondLine);
                txtText = (TextBlock)GetTemplateChild(TxtText);
                stackSecondLineAndArrow = (StackPanel)GetTemplateChild(StackSecondLineAndArrow);
                ribbonButtonLayoutRootClip = (RectangleGeometry)GetTemplateChild(RibbonButtonLayoutRootClip);
                pathMenuSplitClipFigure = (PathFigure)GetTemplateChild(PathMenuSplitClipFigure);
                pathMenuSplitOuterBorderFigure = (PathFigure)GetTemplateChild(PathMenuSplitOuterBorderFigure);
                pathMenuSplitInnerBorderFigure = (PathFigure)GetTemplateChild(PathMenuSplitInnerBorderFigure);

                ribbonButtonLayoutRoot.LayoutUpdated += new EventHandler(ribbonButtonLayoutRoot_LayoutUpdated);

                canvasClickSurface.MouseEnter += new MouseEventHandler(canvasClickSurface_MouseEnter);
                canvasClickSurface.MouseLeave += new MouseEventHandler(canvasClickSurface_MouseLeave);
                canvasClickSurface.MouseLeftButtonDown += new MouseButtonEventHandler(canvasClickSurface_MouseLeftButtonDown);
                canvasClickSurface.MouseLeftButtonUp += new MouseButtonEventHandler(canvasClickSurface_MouseLeftButtonUp);

                canvasMenuSplitClickSurface.MouseEnter += new MouseEventHandler(canvasMenuSplitClickSurface_MouseEnter);
                canvasMenuSplitClickSurface.MouseLeave += new MouseEventHandler(canvasMenuSplitClickSurface_MouseLeave);
                canvasMenuSplitClickSurface.MouseLeftButtonDown += new MouseButtonEventHandler(canvasMenuSplitClickSurface_MouseLeftButtonDown);
                canvasMenuSplitClickSurface.MouseLeftButtonUp += new MouseButtonEventHandler(canvasMenuSplitClickSurface_MouseLeftButtonUp);

                if (!string.IsNullOrEmpty(LargeImageSource))
                    imgLargeIcon.Source = new BitmapImage(new Uri(LargeImageSource, UriKind.Relative));

                if (!string.IsNullOrEmpty(SmallImageSource))
                    imgSmallIcon.Source = new BitmapImage(new Uri(SmallImageSource, UriKind.Relative));

                RenderText();
                InitialiseVisualStyle();

                templateApplied = true;
            }
        }
        #endregion

        #region InitialiseVisualStyle
        private void InitialiseVisualStyle()
        {
            if (Format == ButtonFormat.Large)
            {
                ribbonButtonLayoutRoot.Height = LargeButtonHeight;
                ribbonButtonLayoutRootClip.Rect = new Rect(0d, 0d, ribbonButtonLayoutRootClip.Rect.Width, LargeButtonHeight);

                gridLargeButtonLayout.Visibility = Visibility.Visible;
                gridSmallButtonLayout.Visibility = Visibility.Collapsed;

                canvasClickSurface.Height = LargeButtonHeight - (ButtonMenuStyle != ButtonMenuStyle.Split ? 0d : 29d);

                if (ButtonMenuStyle != ButtonMenuStyle.Split)
                {
                    canvasMenuSplitClickSurface.Height = 0d;
                    canvasMenuSplitClickSurface.SetValue(Canvas.TopProperty, LargeButtonHeight);
                    canvasMenuSplit.Height = SmallButtonHeight;
                    canvasMenuSplit.SetValue(Canvas.TopProperty, 0d);
                }
                else
                {
                    canvasMenuSplitClickSurface.Height = LargeButtonHeight - 35d;
                    canvasMenuSplitClickSurface.SetValue(Canvas.TopProperty, 35d);
                    canvasMenuSplit.Height = LargeButtonHeight - 35d;
                    canvasMenuSplit.SetValue(Canvas.TopProperty, 35d);
                }

                rectInnerBorder.SetValue(Canvas.TopProperty, 0d);
                rectInnerBorder.Height = LargeButtonHeight;
                rectOuterBorder.Height = LargeButtonHeight + 1d;
            }
            else
            {
                ribbonButtonLayoutRoot.Height = SmallButtonHeight;
                ribbonButtonLayoutRootClip.Rect = new Rect(0d, 0d, ribbonButtonLayoutRootClip.Rect.Width, SmallButtonHeight);

                gridLargeButtonLayout.Visibility = Visibility.Collapsed;
                gridSmallButtonLayout.Visibility = Visibility.Visible;

                canvasClickSurface.Height = SmallButtonHeight;

                canvasMenuSplitClickSurface.Height = SmallButtonHeight;
                canvasMenuSplitClickSurface.SetValue(Canvas.TopProperty, 0d);
                canvasMenuSplit.Height = SmallButtonHeight;
                canvasMenuSplit.SetValue(Canvas.TopProperty, 0d);

                rectInnerBorder.SetValue(Canvas.TopProperty, 0.5d);
                rectInnerBorder.Height = SmallButtonHeight - 1d;
                rectOuterBorder.Height = SmallButtonHeight + 1d;

                canvasMenuSplit.Visibility = Visibility.Collapsed;
            }


            if (ButtonMenuStyle == ButtonMenuStyle.None)
            {
                imgMenuDropDownArrowLarge.Visibility = Visibility.Collapsed;
                imgMenuDropDownArrowSmall.Visibility = Visibility.Collapsed;
                canvasMenuSplit.Visibility = Visibility.Collapsed;
                canvasMenuSplitClickSurface.Visibility = Visibility.Collapsed;
            }
            else
            {
                if (Format == ButtonFormat.Large)
                {
                    imgMenuDropDownArrowLarge.Visibility = Visibility.Visible;
                    imgMenuDropDownArrowSmall.Visibility = Visibility.Collapsed;
                }
                else
                {
                    imgMenuDropDownArrowLarge.Visibility = Visibility.Collapsed;
                    imgMenuDropDownArrowSmall.Visibility = Visibility.Visible;
                }

                switch (ButtonMenuStyle)
                {
                    case ButtonMenuStyle.Simple:
                        canvasMenuSplit.Visibility = Visibility.Collapsed;
                        canvasMenuSplitClickSurface.Visibility = Visibility.Collapsed;
                        break;
                    case ButtonMenuStyle.Split:
                        canvasMenuSplit.Visibility = Visibility.Visible;
                        canvasMenuSplitClickSurface.Visibility = Visibility.Visible;
                        break;
                }
            }
        }
        #endregion

        #region SetMouseDownVisualStyle
        private void SetMouseDownVisualStyle(bool overSplitMenu)
        {
            if (overSplitMenu)
            {
                switch (Format)
                {
                    case ButtonFormat.Large:
                        canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonMenuSplitHighlightSelected);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonOuterBorderSelected);
                        break;
                    case ButtonFormat.SmallWithText:
                    case ButtonFormat.Small:
                        canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonMenuSplitHighlightSelected);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonOuterBorderSelected);
                        break;
                }
            }
            else
            {
                switch (Format)
                {
                    case ButtonFormat.Large:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonHighlightAndSelected);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonOuterBorderSelected);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonInnerBorderSelected);
                        break;
                    case ButtonFormat.SmallWithText:
                    case ButtonFormat.Small:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonHighlightAndSelected);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonOuterBorderHighlightAndSelected);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonInnerBorderHighlightAndSelected);
                        break;
                }
            }
        }
        #endregion

        #region SetMouseOverVisualStyle
        private void SetMouseOverVisualStyle(bool overSplitMenu)
        {
            if (overSplitMenu)
            {
                switch (Format)
                {
                    case ButtonFormat.Large:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonSemiHighlight);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonOuterBorder);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonInnerBorder);

                        canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonMenuSplitHighlight);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonMenuSplitOuterBorder);
                        break;
                    case ButtonFormat.SmallWithText:
                    case ButtonFormat.Small:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonSemiHighlight);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonOuterBorder);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonInnerBorder);

                        canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonMenuSplitHighlight);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonMenuSplitOuterBorder);
                        break;
                }
            }
            else
            {
                switch (Format)
                {
                    case ButtonFormat.Large:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonHighlight);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonOuterBorder);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonInnerBorder);

                        canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.LargeButtonMenuSplitSemiHighlight);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.LargeButtonMenuSplitOuterBorder);
                        break;
                    case ButtonFormat.SmallWithText:
                    case ButtonFormat.Small:
                        ribbonButtonLayoutRoot.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonHighlight);
                        rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonInnerBorder);
                        rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonOuterBorder);

                        canvasMenuSplit.Background = Helper.GetBrush(LinearGradientBrushes.SmallButtonMenuSplitSemiHighlight);
                        pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.SmallButtonMenuSplitOuterBorder);
                        break;
                }
            }
        }
        #endregion

        #region ResetVisualStyle
        private void ResetVisualStyle()
        {
            switch (Format)
            {
                case ButtonFormat.Large:
                    ribbonButtonLayoutRoot.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                    rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                    rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);

                    canvasMenuSplit.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                    pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                    break;
                case ButtonFormat.SmallWithText:
                case ButtonFormat.Small:
                    ribbonButtonLayoutRoot.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                    rectOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                    rectInnerBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);

                    canvasMenuSplit.Background = Helper.GetBrush(SolidColorBrushes.Transparent);
                    pathOuterBorder.Stroke = Helper.GetBrush(SolidColorBrushes.Transparent);
                    break;
            }
        }
        #endregion

        #region RenderText
        internal void RenderText()
        {
            dirtyTextLayout = true;

            if (Format == ButtonFormat.Large)
            {
                int indexOfSeperator = Text.IndexOf('|');

                if (indexOfSeperator == -1)
                {
                    int indexOfSpace = Text.IndexOf(' ');

                    if (indexOfSpace == -1)
                    {
                        txtFirstLine.Text = Text;
                        txtSecondLine.Text = "";
                    }
                    else
                    {
                        int nearestIndex = indexOfSpace;
                        int midPointIndex = (int)((Convert.ToDecimal(Text.Length) / 2m) + 2m);
                        int lastAbsDelta = Math.Abs(midPointIndex - indexOfSpace);

                        while (indexOfSpace != -1)
                        {
                            indexOfSpace = Text.IndexOf(' ', indexOfSpace + 1);
                            if (indexOfSpace != -1)
                            {
                                int newAbsDelta = Math.Abs(midPointIndex - indexOfSpace);

                                if (newAbsDelta < lastAbsDelta)
                                {
                                    nearestIndex = indexOfSpace;
                                }
                            }
                        }

                        txtFirstLine.Text = Text.Substring(0, nearestIndex).Trim();
                        txtSecondLine.Text = Text.Substring(nearestIndex + 1).Trim();
                    }
                }
                else
                {
                    txtFirstLine.Text = Text.Substring(0, indexOfSeperator).Trim();
                    txtSecondLine.Text = Text.Substring(indexOfSeperator + 1).Trim();
                }
            }

            if (!string.IsNullOrEmpty(Text) && Format == ButtonFormat.SmallWithText)
                txtText.Text = Text.Replace("|", "").Trim();
            else
                txtText.Text = "";
        }
        #endregion

        #region GenereatePathSegments
        public PathSegmentCollection GeneratePathSegments(bool vertical, Point startPoint, double width, double height, double arcSize)
        {
            PathSegmentCollection segments = new PathSegmentCollection();

            if (vertical)
            {
                segments.Add(Helper.GenerateLineSegment(width + startPoint.X, startPoint.Y));
                segments.Add(Helper.GenerateLineSegment(width + startPoint.X, height - arcSize + startPoint.Y));
                segments.Add(Helper.GenerateArcSegment(SweepDirection.Clockwise, width - arcSize + startPoint.X, height + startPoint.Y, arcSize, arcSize));
                segments.Add(Helper.GenerateLineSegment(arcSize + startPoint.X, height + startPoint.Y));
                segments.Add(Helper.GenerateArcSegment(SweepDirection.Clockwise, startPoint.X, height - arcSize + startPoint.Y, arcSize, arcSize));
            }
            else
            {
                segments.Add(Helper.GenerateLineSegment(width - arcSize + startPoint.X, startPoint.Y));
                segments.Add(Helper.GenerateArcSegment(SweepDirection.Clockwise, width + startPoint.X, arcSize + startPoint.Y, arcSize, arcSize));
                segments.Add(Helper.GenerateLineSegment(width + startPoint.X, height - arcSize + startPoint.Y));
                segments.Add(Helper.GenerateArcSegment(SweepDirection.Clockwise, width - arcSize + startPoint.X, height + startPoint.Y, arcSize, arcSize));
                segments.Add(Helper.GenerateLineSegment(startPoint.X, height + startPoint.Y));
            }

            return segments;
        }
        #endregion

        private EventHandler canExecuteChangedHandler;

        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
          RibbonButton rb = (RibbonButton)d;
          rb.IsEnabled = rb.Command.CanExecute(rb.CommandParameter);

          if (e.OldValue != null)
            ((ICommand)e.OldValue).CanExecuteChanged -= rb.canExecuteChangedHandler;
          if (e.NewValue != null)
            ((ICommand)e.NewValue).CanExecuteChanged += rb.canExecuteChangedHandler;
        }

        private void Command_CanExecuteChanged(object s, EventArgs e)
        {
            this.IsEnabled = this.Command.CanExecute(this.CommandParameter);
        }

        #endregion

        #region Properties...
        #region Text
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        #endregion

        #region Format
        public ButtonFormat Format
        {
            get { return (ButtonFormat)GetValue(FormatProperty); }
            set { SetValue(FormatProperty, value); }
        }
        #endregion

        #region LargeImageSource
        public string LargeImageSource
        {
            get { return (string)GetValue(LargeImageSourceProperty); }
            set { SetValue(LargeImageSourceProperty, value); }
        }
        #endregion

        #region SmallImageSource
        public string SmallImageSource
        {
            get { return (string)GetValue(SmallImageSourceProperty); }
            set { SetValue(SmallImageSourceProperty, value); }
        }
        #endregion

        #region SmallImageWidth
        public double SmallImageWidth
        {
            get { return (double)GetValue(SmallImageWidthProperty); }
            set { SetValue(SmallImageWidthProperty, value); }
        }
        #endregion

        #region MenuID
        public string MenuID
        {
            get
            {
                return (string)GetValue(MenuIDProperty);
            }
            set
            {
                SetValue(MenuIDProperty, value);
            }
        }
        #endregion

        #region ButtonMenuStyle
        public ButtonMenuStyle ButtonMenuStyle
        {
            get
            {
                ButtonMenuStyle menuStyle = ButtonMenuStyle.None;

                if (!string.IsNullOrEmpty(MenuID))
                {
                    if (ButtonClick != null)
                        menuStyle = ButtonMenuStyle.Split;
                    else
                        menuStyle = ButtonMenuStyle.Simple;
                }

                return menuStyle;
            }
        }
        #endregion

        #region ToolTipID
        public string ToolTipID
        {
            get { return (string)GetValue(ToolTipIDProperty); }
            set { SetValue(ToolTipIDProperty, value); }
        }
        #endregion

        #region RibbonTabGroupItemRowSpan
        public override RibbonTabGroupItemRowSpan RibbonTabGroupItemRowSpan
        {
            get
            {
                if (Format == ButtonFormat.Large)
                    return RibbonTabGroupItemRowSpan.Three;
                else
                    return RibbonTabGroupItemRowSpan.One;
            }
        }
        #endregion

        #region ParentRibbon
        public Ribbon ParentRibbon
        {
            get { return parentRibbon; }
            internal set { parentRibbon = value; }
        }
        #endregion

        #region Command
        public ICommand Command
        {
            get
            {
                return (ICommand)base.GetValue(CommandProperty);
            }
            set
            {
                base.SetValue(CommandProperty, value);
            }
        }

        public object CommandParameter
        {
            get
            {
                return base.GetValue(CommandParameterProperty);
            }
            set
            {
                base.SetValue(CommandParameterProperty, value);
            }
        }

        private void ExecuteCommand()
        {
            object commandParameter = this.CommandParameter;
            ICommand command = this.Command;
            if ((command != null) && command.CanExecute(commandParameter))
            {
                command.Execute(commandParameter);
            }
        }
        #endregion

        #endregion
    }
    #endregion
}